﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "Gbmp3DView.h"

#include "DbObjectUtils.h"
#include "Gbmp3DViewInput.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GbmpModelViewExternalDataId.h"
#include "GbmpModelViewUtil.h"
#include "ICopyStrategyReporter.h"
#include "IDocument.h"
#include "IElementParentReporter.h"
#include "IElementStatus.h"
#include "IExternalDataComponent.h"
#include "ILevel.h"
#include "IModelView.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IPlane.h"
#include "ISectionBox.h"
#include "IWorkPlane.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "IUserCoordinateSystem.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"
using namespace gcmp;

DEFINE_MODEL_VIEW_BASE_METHODS(Gbmp3DView)

DBOBJECT_DATA_DEFINE(Gbmp3DView)
{
    m_pOwnerElement = nullptr;
    SetEnableSectionBox__(false);
}

Gbmp3DView* Gbmp3DView::Create(const Gbmp3DViewInput& input)
{
    const ILevel* pLevel = input.Level;
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");

    Vector3d viewOrigin(0, 0, pLevel->GetElevation());
    IModelView* pModelView = GbmpModelViewUtil::CreateModelView(input.Document, input.Name, BuiltInViewType::ThreeDimensional,
        input.ViewDirection, viewOrigin, input.RightDirection, input.UpDirection);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    pModelView->SetUserDataId(input.ViewType);


    // 默认工作平面为XOY平面
    OwnerPtr<IPlane> opWorkPlane = IPlane::Create(pModelView->GetOrigin(), Vector3d::UnitX, Vector3d::UnitY);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opWorkPlane, L"opWorkPlane为空",L"GDMPLab",L"2024-03-30");
    bool bOk = pModelView->SetWorkPlane(opWorkPlane.get());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"SetWorkPlane失败",L"GDMPLab",L"2024-03-30");

    IUserCoordinateSystem* pUCS = IUserCoordinateSystem::Create(input.Document, input.Name, viewOrigin, input.RightDirection, input.UpDirection);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUCS, L"pUCS为空",L"GDMPLab",L"2024-03-30");

    Gbmp3DView* p3DView = Gbmp3DView::RegisterExternalData(pModelView);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(p3DView, L"p3DView为空",L"GDMPLab",L"2024-03-30");

    p3DView->SetUserViewTypeName(input.ViewType);
    p3DView->SetWorkLevelId(pLevel->GetOwnerElement()->GetElementId());

    pModelView->BindUserCoordinateSystem(pUCS->GetElementId());

    return p3DView;
}

Gbmp3DView* Gbmp3DView::Get(IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    if (IExternalDataComponent * pExternalDataComponent = pModelView->GetExternalDataComponent())
    {
        IExternalData* pData = pExternalDataComponent->FindExternalData(MODEL_VIEW_EXTERNAL_DATA_ID);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pData, L"pData为空",L"GDMPLab",L"2024-03-30");
        return quick_cast<Gbmp3DView>(pData);
    }
    return nullptr;
}

const Gbmp3DView* Gbmp3DView::Get(const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    if (const IExternalDataComponent * pExternalDataComponent = pModelView->GetExternalDataComponent())
    {
        const IExternalData* pData = pExternalDataComponent->FindExternalData(MODEL_VIEW_EXTERNAL_DATA_ID);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pData, L"pData为空",L"GDMPLab",L"2024-03-30");
        return quick_cast<const Gbmp3DView>(pData);
    }
    return nullptr;
}

Gbmp3DView* Gbmp3DView::RegisterExternalData(IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<Gbmp3DView> op3DView = NEW_AS_OWNER_PTR(Gbmp3DView);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(op3DView, L"op3DView为空",L"GDMPLab",L"2024-03-30");

    op3DView->SetOwnerElement(pModelView);

    Gbmp3DView* p3DView = op3DView.get();
    if (IExternalDataComponent * pExternalDataComponent = pModelView->GetExternalDataComponent())
    {
        pExternalDataComponent->RegisterExternalData(MODEL_VIEW_EXTERNAL_DATA_ID, TransferOwnership(op3DView));
    }
    return p3DView;
}

bool Gbmp3DView::SetWorkLevelId(ElementId workLevelId)
{
    SetWorkLevelId__(workLevelId);

    // 平面视图设置工作楼层，同时设置相应工作平面
    IModelView* pModelView = quick_cast<IModelView>(GetOwnerElement());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    IWorkPlane* pWorkPlane = quick_cast<IWorkPlane>(pModelView->GetDocument()->GetElement(pModelView->GetWorkPlaneId()));
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pWorkPlane, L"pWorkPlane为空",L"GDMPLab",L"2024-03-30");
    pWorkPlane->SetReferencedPlaneId(workLevelId);

    return true;
}

bool Gbmp3DView::SetIsCroppedByRendering(bool isCroppedByRendering)
{
    IModelView* pModelView = quick_cast<IModelView>(this->GetOwnerElement());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    pModelView->SetIsRenderCropEnabledForSectionBox(isCroppedByRendering);
    return true;
}

bool Gbmp3DView::IsCroppedByRendering() const
{
    const IModelView* pModelView = quick_cast<IModelView>(this->GetOwnerElement());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    return pModelView->IsRenderCropEnabledForSectionBox();
}

bool Gbmp3DView::EnableSectionBox(bool enable)
{
    // 如果SectionBox对象被设置为不可见，则将其改为可见。
    // 如果SectionBox已经可见了，则设置其视图可见性。
    IDocument* pDoc = this->GetDocument();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    IModelView* pModelView = quick_cast<IModelView>(this->GetOwnerElement());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    ISectionBox* pSectionBox = quick_cast<ISectionBox>(pDoc->GetElement(pModelView->GetSectionBoxId()));
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pSectionBox, L"pSectionBox为空",L"GDMPLab",L"2024-03-30");

    const IElementStatus* pStatus = pSectionBox->GetStatus();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pStatus, L"无法获取状态集",L"GDMPLab",L"2024-03-30");
    if (!pStatus->IsVisible())
    {
        pSectionBox->GetStatus()->SetIsVisible(true);
    }
    pModelView->SetElementVisibilityByElementId(pSectionBox->GetElementId(), enable);

    if (enable)
    {
        Box3d sceneBox;
        bool bOk = GbmpModelViewUtil::GetAllVisibleGraphicsShapesBoundingBox(pModelView, sceneBox);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"包围盒计算失败",L"GDMPLab",L"2024-03-30");
        if (sceneBox.IsEmpty())
        {
            Vector3d minPoint;
            Vector3d maxPoint;
            {
                double length = 8000.0f;
                double width = 6000.0f;
                double height = 6000.0f;
                minPoint = Vector3d(-length / 2, -width / 2, -height / 2);
                maxPoint = Vector3d(length / 2, width / 2, height / 2);
            }
            sceneBox = Box3d(minPoint, maxPoint);
        }

        Coordinate3d coord;
        TiltBox3d tiltBox = TiltBox3d::Create(coord, sceneBox);
        pSectionBox->SetTiltBox(tiltBox);
    }

    SetEnableSectionBox__(enable);
    return true;
}

void Gbmp3DView::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    FOR_EACH(deletedElementId, deletedElementIds)
    {
        if (deletedElementId == GetWorkLevelId__())
        {
            ElementId nextWorkLevelId = GbmpModelViewUtil::Get3DViewNextWorkLevel(GetDocument(), deletedElementId);
            DBG_WARN_AND_RETURN_VOID_UNLESS(nextWorkLevelId.IsValid(), L"nextWorkLevelId无效",L"GDMPLab",L"2024-03-30");
            SetWorkLevelId(nextWorkLevelId);
            break;
        }
    }
}

void Gbmp3DView::ReportParents(IElementParentReporter& reporter) const
{
    reporter.ReportWeak(GetWorkLevelId());
}

void Gbmp3DView::GetCalculators(ICalculatorCollection * calculators) const
{
}

void Gbmp3DView::ReportParameterDefinitions(std::vector<int>* pParamDefIds) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParamDefIds, L"pParamDefIds为空",L"GDMPLab",L"2024-03-30");
    pParamDefIds->push_back(PARAMETER_ID(GbmpViewWorkLevelBuiltInParameter));
    pParamDefIds->push_back(PARAMETER_ID(ModelViewSectionBoxBuiltInParameter));
    pParamDefIds->push_back(PARAMETER_ID(SectionBoxCropByRenderingBuiltInParameter));
}

OwnerPtr<IParameter> Gbmp3DView::GetNativeParameter(int paramDefId) const
{
    OwnerPtr<IParameter> param;
    if (paramDefId == PARAMETER_ID(GbmpViewWorkLevelBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::ElementId, false, false, 
            //true, PARAMETER_UID(GbmpViewWorkLevelBuiltInParameter), ParameterProcessType::GeneralInput);
        ParameterAttributes pa = {false, false, true};
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::ElementId, 
            pa, PARAMETER_UID(GbmpViewWorkLevelBuiltInParameter), ParameterProcessType::GeneralInput);
        param->SetValueAsElementId(GetWorkLevelId());
        return TransferOwnership(param);
    }
    else if (paramDefId == PARAMETER_ID(ModelViewSectionBoxBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::Bool, false, false,
            //true, PARAMETER_UID(ModelViewSectionBoxBuiltInParameter), ParameterProcessType::GeneralInput);
        ParameterAttributes pa = {false, false, true};
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Bool, pa, 
            PARAMETER_UID(ModelViewSectionBoxBuiltInParameter), ParameterProcessType::GeneralInput);
        param->SetValueAsBool(IsSectionBoxEnabled());
        return TransferOwnership(param);
    }
    else if (paramDefId == PARAMETER_ID(SectionBoxCropByRenderingBuiltInParameter))
    {
        ParameterAttributes pa = { false, false, true };
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Bool, pa,
            PARAMETER_UID(SectionBoxCropByRenderingBuiltInParameter), ParameterProcessType::GeneralInput);
        param->SetValueAsBool(IsCroppedByRendering());
        return TransferOwnership(param);
    }
    return nullptr;
}

bool Gbmp3DView::SetNativeParameter(const IParameter *param, std::wstring* errorMsg/* = nullptr*/)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(param, L"param is null.",L"GDMPLab",L"2024-03-30");

    if (param->GetParameterDefinitionId() == PARAMETER_ID(GbmpViewWorkLevelBuiltInParameter))
    {
        SetWorkLevelId(param->GetValueAsElementId());
        return true;
    }
    else if (param->GetParameterDefinitionId() == PARAMETER_ID(ModelViewSectionBoxBuiltInParameter))
    {
        EnableSectionBox(param->GetValueAsBool());
        return true;
    }
    else if (param->GetParameterDefinitionId() == PARAMETER_ID(SectionBoxCropByRenderingBuiltInParameter))
    {
        SetIsCroppedByRendering(param->GetValueAsBool());
        return true;
    }
    return false;
}

bool Gbmp3DView::IsParameterModifiable(int paramDefId) const
{
    return true;
}

void Gbmp3DView::Report(const ICopyContext& copyContext, ICopyStrategyReporter& reporter) const
{
    reporter.ReportToCopy(GetOwnerElementId());
    reporter.ReportToKeep(GetWorkLevelId());
}

IDocument* Gbmp3DView::GetDocument() const
{
    return m_pOwnerElement ? m_pOwnerElement->GetDocument() : nullptr;
}

ElementId Gbmp3DView::GetOwnerElementId() const
{
    return m_pOwnerElement ? m_pOwnerElement->GetElementId() : ElementId::InvalidID;
}

NdbObject* Gbmp3DView::GetTopOwnerObject() const
{
    if (!GetDocument())
        return nullptr;

    return quick_cast<NdbObject>(GetDocument()->GetElement(GetOwnerElementId()));
}
